<?php

class Shell_Shell {

			private static $latest_instance;
	
			//Array of the commands
			private $commands;
			private $fs;
												  
//*************************************************************************************************
		public function __construct() 
		{
			// set the shell prompt
			Core_IO::$prompt = "kohv-shell> ";
			Core_IO::$trimEnabled = true;
			
			// get all the available commands
			$this->collectCommands();
			
			// initialize 'file system'
			$fsRoot = Core_Registry::read(Enum_RegistryItems::FS_ROOT_DIR); 
			$this->fs = new System_FS($fsRoot);
			Core_Registry::write(Enum_RegistryItems::FILE_SYSTEM, $this->fs);
			
			self::$latest_instance = $this;
		}

//*************************************************************************************************
/**
 * 1. Reads the content of the 'Command' directory 
 * 2. Finds the files with the command classes
 * 3. Instantiates the found commands and populates $this->commands with them
 */
		private function collectCommands() 
		{
			$this->commands = array();
			$pathToCommands = getcwd()."/Shell/Command";
			
			$commandDir = opendir( $pathToCommands ); 
			
			while ( ($file = readdir($commandDir)) !== false )
			{
				$extension = strtolower(substr(strrchr($file, '.'), 1)); 
				if($extension == 'php')
				{ 	
					// eliminate file extension				
					$commandClassName = str_replace(".php", "", $file);
					
					//skip the command base class and the command inteface
					/*if ($commandClassName == "CommandBase" || $commandClassName == "IShellCommand" ) 
						continue;
					*/
					// capitalize the first letter of the class name
					$commandClassName = ucfirst($commandClassName); 
					
					// add the command class name prefix
					$commandClassName = "Shell_Command_".$commandClassName;					 
					
					$command = new $commandClassName; 
					$commandName = $command->getName();
					$this->commands[$commandName] = $command; 
				} 
			}
			
			closedir($commandDir);
		}
		
//*************************************************************************************************
		public function start($clearScreen = true) 
		{
		
			// clear screen if needed
			if ( $clearScreen && $this->commandExists("clear") ) 
			{
				$this->commands["clear"]->execute(array());
			}
		
			$figlet = new Zend_Text_Figlet();
			echo "\n\n".$figlet->render('kohv', 'ascii') .$figlet->render('shell', 'ascii');
			
			$ver = Core_Registry::read(Enum_RegistryItems::VERSION);
			
			$greeting = "Welcome to kohv-shell v".$ver.". Type 'help' for help.";
			$greeting = Core_StringUtils::underline($greeting, '*');
			echo "\n\n".$greeting."\n\n";

			// main user input reading loop
			while(1)
			{
				$userInput = Core_IO::readUserInput();
				
				if ($userInput != "")
				{
						$args = $this->parseArgs( $userInput );
						$commandName = array_shift($args);
						
						if ($this->commandExists($commandName))
						{
							$this->commands[$commandName]->execute($args);
						}
						else
						{
							echo "Command not recognized: " . $commandName . "\n\n";
						}
				}
			}
			
			// user input is no longer needed
			Core_IO::close();		
		}

		
//*************************************************************************************************
/**
 * 
 * Parses the user input. Takes into account the compound arguments, i.e. the ones in quotes and with the spaces.
 * Example of an option with a compound argument:   -a "first thing first"
 * Here "first thing first" will be taken as a single argument.
 * 
 * @param $argsString
 */
		private function parseArgs($argsString) 
		{
			
			/*
			// simple parsing, ignores the compound arguments like: -a "first thing first"
			$args = explode ( " " , $argsString );
			return $args;
			*/ 
			
 
			$args = array();
			$curArg = "";
			$char = "";
			
			// flag to indicate if an argument is compound, i.e. has spaces and is surrounded with the quotes.
			$compoundArgFlag = false;
			
			$argStrLen = strlen($argsString);
			for($i=0; $i<$argStrLen; $i++)
			{
				$char = $argsString[$i]; 

				if ($char !== ' ' && $char !== '"')
				{
					$curArg .= $argsString[$i];
					continue;
				}
				
				if ($char === '"')
				{
					$compoundArgFlag = !$compoundArgFlag;
					continue;
				}
				
				if ($char === ' ' && $compoundArgFlag === false)
				{
					array_push($args, trim($curArg));
					$curArg = "";
					continue;
				}
					
				if($char === ' ' && $compoundArgFlag === true)
				{
					$curArg .= ' ';
					continue;
				}
				
				echo "\nERROR: Unable to parse the arguments!\n";
			}
			
			if ( strlen($curArg) > 0 )
			{
					array_push($args, trim($curArg));
					$curArg = "";
			}
			
			return  $args;
		}
		
//*************************************************************************************************
/** Returns true if a command with the given name was defined, false otherwise */
		private function commandExists($commandName) 
		{
			foreach ($this->commands as $name => $value) 
			{
				if ($name == $commandName) return true;
			}			
			return false;
		}

		
//*************************************************************************************************
		public function __toString() 
		{
		  	$reg = Core_Registry::__toArray();
		  	
			// trying to make a table			
			$table = new Zend_Text_Table(array('columnWidths' => array(20, 60)));
			$table->setDecorator('ascii');
			$table->appendRow( array(' COMMAND', ' DETAILS') );
			
			foreach ($this->commands as $key => $value) 
			{
				$table->appendRow( array( ' '.(string)$key, ' '.(string)$value ) );	
			}
			
			return "\nShell commands summary:\n" . $table->render();
		}
		
		
//*************************************************************************************************
/** Returns the latest instance of Shell */
		public static function getLatestInstance()
		{
			return self::$latest_instance;
		}
}